/* 
 * File:   Utility.cpp
 * Author: Bram
 * 
 * Created on January 10, 2013, 10:43 PM
 */

#include "Utility.h"
#include "Config.h"

Utility* Utility::instance = 0;

Utility::Utility(void)
{
}


Utility::~Utility(void)
{
}

vector<string> Utility::split(const string &s, char delim) {
	vector<string> elems;
	return split(s, delim,elems);
}

vector<string> &Utility::split(const string &s, char delim, vector<string> &elems) {
	stringstream ss(s);
	string item;
	while(getline(ss, item, delim)) {
		elems.push_back(item);
	}
	return elems;
}


bool Utility::startsWith(const string& haystack, const string& needle) {
	return needle.length() <= haystack.length()  && equal(needle.begin(), needle.end(), haystack.begin());
}

bool Utility::endsWith(const string& haystack, const string& needle) {
	return needle.length() <= haystack.length()  &&  (0 == haystack.compare (haystack.length() - needle.length(), needle.length(), needle));
}

bool Utility::contains(const string& haystack, const string& needle) {
	return (haystack.find(needle) != string::npos);
}

string Utility::convertInt(int number)
{
	stringstream ss;//create a stringstream
	ss << number;//add number to the stream
	return ss.str();//return a string with the contents of the stream
}

string Utility::convertLong(long number)
{
	stringstream ss;//create a stringstream
	ss << number;//add number to the stream
	return ss.str();//return a string with the contents of the stream
}

const char * const Utility::BoolToString(bool b)
{
  return b ? "true" : "false";
}

string Utility::FullFilePathToFileName(const string& fullFilePath){
  string filename;

  size_t pos = fullFilePath.find_last_of("\\");
  if(pos != string::npos)
  {
      filename.assign(fullFilePath.begin() + pos + 1, fullFilePath.end());
  } else {
    size_t pos = fullFilePath.find_last_of("/");
    if(pos != string::npos)
    {
        filename.assign(fullFilePath.begin() + pos + 1, fullFilePath.end());
    }
    else {
      filename = fullFilePath;
    }
  }
  
  return ((filename.size() > 0) ? filename : fullFilePath);
}

//vind de laatste slash en haalt alles wat ervoor staat weg, afhankelijk van removeLastSlash, laat ie de laatste slash wel of niet staan
bool Utility::FileToPath(string& fullFilePath, bool removeLastSlash){
  string filename;
  bool isPath = false;
  int intRLS =  ((!removeLastSlash) ? 1 : 0);
  
  size_t pos = fullFilePath.find_last_of("\\");
  if(pos != string::npos)
  {
      filename.assign(fullFilePath.begin(), fullFilePath.begin() + pos + intRLS);
      isPath = true;
  } else {
    size_t pos = fullFilePath.find_last_of("/");
    if(pos != string::npos)
    {
        filename.assign(fullFilePath.begin(), fullFilePath.begin() + pos + intRLS);
        isPath = true;
    }
    else {
      filename = fullFilePath;
    }
  }
  
  if(filename.size() > 0){
      fullFilePath = filename;
  }
  
  return isPath;
}

void Utility::toUpper(string& strToUpper){
        std::transform(strToUpper.begin(), strToUpper.end(),strToUpper.begin(), ::toupper);
}

void Utility::toLower(string& strToLower){
        std::transform(strToLower.begin(), strToLower.end(),strToLower.begin(), ::toupper);
}

bool Utility::directoryExists(const string& folderName){
    string newFolderName = folderName;
    fixPathLinuxWindowsProblem(newFolderName, true);
    
    struct stat status;
    stat( newFolderName.c_str(), &status );
    
    if ( access( newFolderName.c_str(), 0 ) == 0 ){
        if ( status.st_mode & S_IFDIR )
        {
           //cout << "The directory exists." << endl;
           return true;
        }
        else
        {
           //cout << "The path you entered is a file." << endl;
        }
    } else {
        //cout << "Path doesn't exist." << endl;
    }
    return false;
}

//maakt alle directorys aan die nodig zijn voor de fullDir!
bool Utility::createDir(const string& dirPath, const string& bPath)
{
    string fullDirPath = dirPath;
    fixPathLinuxWindowsProblem(fullDirPath, true);
    string basePath = bPath;
    fixPathLinuxWindowsProblem(basePath, true);
    if(fullDirPath.size() == basePath.size()){
        mkdir(fullDirPath.c_str(), 0755);
    } else {
        string strToSplit = fullDirPath.substr(basePath.size());
        stringstream ss;
        vector<std::string> vstrings = split(strToSplit,Config::getInstance()->PathSeparator[0]);
        
        stringstream ss2;
        for(int i = 0; i < vstrings.size(); i++){
            ss.str("");
            ss.clear();
            ss2 << vstrings[i] << Config::getInstance()->PathSeparator;
            ss << basePath << ss2.str();
            string currDir = ss.str();
            mkdir(currDir.c_str(), 0755);
        }
        std::copy(vstrings.begin(), vstrings.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
        //cout << "vstrings::: " << vstrings.size() << endl;
    }
    
    
    //int result = mkdir(fullDirPath.c_str(),0755);
    return true;
}

bool Utility::isDirectoryEmpty(const string& dirname)
{
    int n = 0;
    struct dirent *d;
    DIR *dir = opendir(dirname.c_str());
    //Not a directory or doesn't exist
    if (dir == NULL) {
        return false;
    }
    
    while ((d = readdir(dir)) != NULL) {
        if(++n > 2){
            break;
        }
    }
    closedir(dir);
    
    //Directory Empty
    if (n <= 2) {
        return true;
    }
    
    return false;
}

//verwijderd alle lege mappen vanaf het opgegeven path, hoger opgelegen dus
void Utility::removeEmptyFoldersInUpperPath(const string& basePath)
{
    string newBasePath = basePath;
    fixPathLinuxWindowsProblem(newBasePath, true); //fixt de slash op het einde!
    vector<string> folders;
    Utility::getInstance()->getFoldersInDirIncludingSubdirs(newBasePath, folders);
    for(int i = 0; i < folders.size(); i++){
        stringstream ss;
        ss << basePath << Config::getInstance()->PathSeparator << folders[i];
        string fullFolderPath = ss.str();
        //if dier is empty
        if(isDirectoryEmpty(fullFolderPath)){
            if(rmdir(fullFolderPath.c_str()) == 0){ //haalt de dir weg
                //succeeded
                //cout << "removed: " << fullFolderPath << endl;
            } else {
                //failed
            }
        }
    }
}

bool Utility::isLastFileInFolder(const string& fullFilePath)
{    
    vector<std::string> vstrings = split(fullFilePath,Config::getInstance()->PathSeparator[0]);
    
    if(fileExists(fullFilePath) && vstrings.size() > 0){
        string fileName = vstrings[vstrings.size()-1];
        string folderPath = fullFilePath.substr(0, fullFilePath.size() - fileName.size());
        
        vector<string> filesAndFolders;
        getFilesInDirIncludingSubdirs(folderPath, filesAndFolders, false); // haalt alle files en mappen op
        for(int i = 0; i < filesAndFolders.size(); i++){
            string st1 = filesAndFolders[i];
            //als de map / file NIET gelijk is aan de huidige file is het niet de enige
            if(!(filesAndFolders[i].compare(fileName) == 0)){
                return false;
            }
        }
        return true;
    }
    return false;
}

bool Utility::fileExists(const string& fileName)
{
	bool fileExists = true;
	ifstream file(fileName.c_str());
	if (!file) 
	{
		fileExists = false;
	}
	file.close();
	return fileExists;
}

void Utility::getFilesFoldersInDir(const string &path, vector<string> &foundFilesFolders, bool doTimeStamped)
{
    DIR *dir;
    string pathSEP = Config::getInstance()->PathSeparator;
    struct dirent *ent;
    dir = opendir (path.c_str());
    if (dir != NULL) {
        /* print all the files and directories within directory */
        while ((ent = readdir (dir)) != NULL) {
                //printf ("%s\n", ent->d_name);
            string strFileOrFolder = string(ent->d_name);
            if(!((strFileOrFolder.compare(".") == 0) || (strFileOrFolder.compare("..") == 0))){
                
                stringstream ss;
                ss << path;
                if(!Utility::getInstance()->endsWith(path, pathSEP))
                {
                    ss << Config::getInstance()->PathSeparator;
                }
                ss << string(ent->d_name);
                string dasPath = ss.str();
                struct stat status;
                stat( dasPath.c_str(), &status );
                
                if(doTimeStamped){
                    stringstream ss;
                    ss << dasPath << "|" << status.st_mtim.tv_sec;
                    foundFilesFolders.push_back(ss.str());
                } else {
                    foundFilesFolders.push_back(dasPath);
                }
                //foundFilesFolders.push_back(strFileOrFolder);
            }
        }
        closedir(dir);
    } else {
        foundFilesFolders.clear();
        foundFilesFolders.push_back("DIRECTORY NOT EXISTS");
    }
}


//normaal haalt ie het volledige path op, hier kapt ie het af tot het relatieve path!
void Utility::getFilesInDirIncludingSubdirs(const string &path, vector<string> &foundFilesFolders, bool doTimeStamped)
{
    vector<string> dasFiles;
    getFilesSubdirsPRIVATE(path, dasFiles, doTimeStamped);
    if(dasFiles.size() > 0 && !Utility::getInstance()->startsWith(string(dasFiles[0]), "ERROR")){
        for(int i = 0; i < dasFiles.size(); i++)
        {
            string fullFilePath = dasFiles[i];
            if(fullFilePath.size() > path.size())
            {
                fullFilePath = fullFilePath.substr(path.size());
                //fullFilePath = fullFilePath.substr(path.size()+1);
            }
            foundFilesFolders.push_back(fullFilePath);
        }
    }
}

void Utility::getFilesSubdirsPRIVATE(const string &path, vector<string> &foundFilesFolders, bool doTimeStamped)
{
	DIR *dir;
	struct dirent *ent;
	dir = opendir (path.c_str());
	if (dir != NULL) {
		/* print all the files and directories within directory */
		while ((ent = readdir (dir)) != NULL) {
			//printf ("%s\n", ent->d_name);
                    string dasFileOrDir = string(ent->d_name);
                    //filtert de . en .. dirs eruit
                    if(!((dasFileOrDir.compare(".") == 0) || (dasFileOrDir.compare("..") == 0)))
                    {
                        stringstream ss;
                        ss << path;
                        if(!Utility::getInstance()->endsWith(path, Config::getInstance()->PathSeparator))
                        {
                            ss << Config::getInstance()->PathSeparator;
                        }
                        ss << string(ent->d_name);
                        string dasPath = ss.str();
                        struct stat status;
                        stat( dasPath.c_str(), &status );
                        
                        bool isDir = false;
                        if ( access( dasPath.c_str(), 0 ) == 0 ){
                            if ( status.st_mode & S_IFDIR )
                            {
                                getFilesSubdirsPRIVATE(dasPath, foundFilesFolders, doTimeStamped);
                                isDir = true;
                            }
                        }

                        if(!isDir && (!((dasPath.compare(".") == 0) || (dasPath.compare("..") == 0))) ){
                            if(doTimeStamped){
                                stringstream ss;
                                ss << dasPath << "|" << status.st_mtim.tv_sec;
                                foundFilesFolders.push_back(ss.str());
                            } else {
                                foundFilesFolders.push_back(dasPath);
                            }
                        }
                        
                    }
		}
		closedir(dir);
	} else {
		//foundFilesFolders.clear();
                foundFilesFolders.push_back("ERROR: DIRECTORY NOT EXISTS");
	}
}


//haalt alle mappen op, inclusief submappen
//normaal haalt ie het volledige path op, hier kapt ie het af tot het relatieve path!
void Utility::getFoldersInDirIncludingSubdirs(const string &path, vector<string> &foundFolders)
{
    vector<string> dasFolders;
    getFoldersSubdirsPRIVATE(path, dasFolders);
    if(dasFolders.size() > 0 && !Utility::getInstance()->startsWith(string(dasFolders[0]), "ERROR")){
        for(int i = 0; i < dasFolders.size(); i++)
        {
            string fullFilePath = dasFolders[i];
            if(fullFilePath.size() > path.size())
            {
                fullFilePath = fullFilePath.substr(path.size());
                //fullFilePath = fullFilePath.substr(path.size()+1);
            }
            foundFolders.push_back(fullFilePath);
        }
    }
}

void Utility::getFoldersSubdirsPRIVATE(const string &path, vector<string> &foundFolders)
{
	DIR *dir;
	struct dirent *ent;
	dir = opendir (path.c_str());
	if (dir != NULL) {
		/* print all the files and directories within directory */
		while ((ent = readdir (dir)) != NULL) {
			//printf ("%s\n", ent->d_name);
                    string dasFileOrDir = string(ent->d_name);
                    //filtert de . en .. dirs eruit
                    if(!((dasFileOrDir.compare(".") == 0) || (dasFileOrDir.compare("..") == 0)))
                    {
                        stringstream ss;
                        ss << path;
                        if(!Utility::getInstance()->endsWith(path, Config::getInstance()->PathSeparator))
                        {
                            ss << Config::getInstance()->PathSeparator;
                        }
                        ss << string(ent->d_name);
                        string dasPath = ss.str();
                        struct stat status;
                        stat( dasPath.c_str(), &status );

                        bool isDir = false;
                        if ( access( dasPath.c_str(), 0 ) == 0 ){
                            if ( status.st_mode & S_IFDIR )
                            {
                                getFoldersSubdirsPRIVATE(dasPath, foundFolders);
                                isDir = true;
                            }
                        }

                        if(isDir){
                            foundFolders.push_back(dasPath);
                        }
                        
                    }
		}
		closedir(dir);
	} else {
		//foundFilesFolders.clear();
                foundFolders.push_back("ERROR: DIRECTORY NOT EXISTS");
	}
}

string &Utility::ltrim(string &s) {
	s.erase(s.begin(), find_if(s.begin(), s.end(), not1(ptr_fun<int, int>(isspace))));
	return s;
}
string &Utility::rtrim(string &s) {
	s.erase(find_if(s.rbegin(), s.rend(), not1(ptr_fun<int, int>(isspace))).base(), s.end());
	return s;
}
string &Utility::trim(string &s) {
	return ltrim(rtrim(s));
}

/* The Itoa code is in the puiblic domain */
char* Utility::Itoa(int value, char* str, int radix) {
    static char dig[] =
        "0123456789"
        "abcdefghijklmnopqrstuvwxyz";
    int n = 0, neg = 0;
    unsigned int v;
    char* p, *q;
    char c;
    if (radix == 10 && value < 0) {
        value = -value;
        neg = 1;
    }
    v = value;
    do {
        str[n++] = dig[v%radix];
        v /= radix;
    } while (v);
    if (neg)
        str[n++] = '-';
    str[n] = '\0';
    for (p = str, q = p + n/2; p != q; ++p, --q)
        c = *p, *p = *q, *q = c;
    return str;
}

void Utility::fixPathLinuxWindowsProblem(string& fullPath, bool fixEnd)
{

    //uit voorzorg vervangt ie op windows de slash met de backslash
    //op linux vice versa
    #if defined _OS_WIN
        replace(fullPath.begin(), fullPath.end(), '/', '\\');
    #else
        replace(fullPath.begin(), fullPath.end(), '\\', '/');
    #endif
    
        if(fixEnd && !endsWith(fullPath, Config::getInstance()->PathSeparator)){
            stringstream ss;
            ss << fullPath << Config::getInstance()->PathSeparator;
            fullPath = ss.str();
        }
}